package net.courier.remoting.client;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import net.courier.remoting.ProxyLookuper;
import net.sf.cglib.proxy.Enhancer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DefaultProxyLookuper implements ProxyLookuper {
	private static final Logger LOGGER = LoggerFactory.getLogger(DefaultProxyLookuper.class);
	private final int PROXY_CACHE_SIZE = 62;
	private Map<String, Object> proxyCache = new ConcurrentHashMap<String, Object>(PROXY_CACHE_SIZE);
	
	private ClientPoolFactory clientPoolFactory = new ClientPoolFactory();
	
	public void setClientFacotry(ClientFactory clientFactory) {
		this.clientPoolFactory.setClientFactory(clientFactory);
	}
	
	public DefaultProxyLookuper() {
		
	}

	@Override
	public <T> T lookup(String uri, Class<T> clazz) {
		try {
			URI u = new URI(uri);
			LocationInfo info = new LocationInfo(u);
			return lookup(info.host, info.port, info.id, clazz);
		} catch (URISyntaxException e) {
			LOGGER.error("无法解析指定的URI", e);
			return null;
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> T lookup(String host, int port, String id, Class<T> clazz) {
		String uri = host + ":" + port + "/" + id;
		T instance = (T) proxyCache.get(uri);
		if (instance == null || !clazz.isAssignableFrom(instance.getClass())) {
			ProxyInterceptor interceptor = newProxyInterceptor(host, port, id);
			instance = (T) Enhancer.create(clazz, interceptor);
			proxyCache.put(uri, instance);
		}
		return instance;
	}


	protected ProxyInterceptor newProxyInterceptor(String host, int port, String id) {
		return new ProxyInterceptor(id, clientPoolFactory.generateClientPool(host, port));
	}


	protected static class LocationInfo {
		protected String host;
		protected int port;
		protected String id;

		protected LocationInfo(URI uri) {
			host = uri.getHost();
			port = uri.getPort();
			String path = uri.getPath();
			if (path == null || "".equals(path) || !path.endsWith("/")) {
				id = path;
			} else {
				id = path.substring(0, path.length() - 1);
			}
			if (path != null && id.startsWith("/")) {
				id = id.substring(1);
			}
		}
	}

}
